package org.jeecg.modules.cosmetic.handler;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.dto.CategoryDTO;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.util.DateUtils;
import org.jeecg.modules.cosmetic.constant.ExplainConstants;
import org.jeecg.modules.cosmetic.constant.enums.*;
import org.jeecg.modules.cosmetic.entity.*;
import org.jeecg.modules.cosmetic.model.dto.IngredientContentDTO;
import org.jeecg.modules.cosmetic.service.*;
import org.jeecg.modules.cosmetic.utils.XwpfTUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author scx
 * @date 2022/7/22 9:56 上午
 */
@Service
@Slf4j
public class WordReportHandler {

    @Resource
    private ITProductService productService;
    @Resource
    private ITEvaluatorService evaluatorService;
    @Resource
    private ITFormulaService formulaService;
    @Resource
    private ITFormulaRiskService formulaRiskService;
    @Resource
    private ITSecurityEvaluationService securityEvaluationService;
    @Resource
    private ITRawmaterialService rawmaterialService;
    @Resource
    private ITProductMaterialService productMaterialService;
    @Resource
    private ISysBaseAPI sysBaseApiImpl;
    @Value(value = "${jeecg.path.upload}")
    private String uploadPath;

    @Value(value = "${formula.harmful.matter}")
    private String harmfulMatters;

    /**
     * 生成报告
     *
     * @param productId
     * @return
     */
    public Result<?> generateReport(Long productId) {
        TProduct product = productService.getById(productId);
        Optional.ofNullable(product).orElseThrow(() -> new JeecgBootException("报告生成异常-产品不存在"));
        if (product.getStatus() == ProductStatusEnum.FINISHED.getCode() && StringUtils.isNotEmpty(product.getReportUrl())) {
            return Result.ok(product.getReportUrl());
        }
        String fileUrl = asyncBuildReportData(product);
        if (StringUtils.isEmpty(fileUrl)) {
            throw new JeecgBootException("生成报告失败,报告地址为空");
        }
        product.setReportUrl(fileUrl);
        product.setStatus(ProductStatusEnum.FINISHED.getCode());
        productService.updateById(product);
        return Result.ok(fileUrl);
    }


    /**
     * 生成安评报告word
     *
     * @param product
     */
//    @Async
    public String asyncBuildReportData(TProduct product) {
        log.info("报告生成开始-----{}", product.getChineseName());
        Map<String, Object> params = Maps.newHashMap();
        params.put("${cName}", product.getChineseName());
        params.put("${iecicName}", product.getEnglishName());
        params.put("${filingName}", product.getFilingName());
        params.put("${filingAddress}", product.getFilingAddress());
        params.put("${type}", CosmeticTypeEnum.getCosmeticTypeByType(product.getType()).getMsg());
        params.put("${productType}", ProductTypeEnum.getProductTypeEnumByType(product.getProductType()).getMsg());
        params.put("${applyPart}", product.getApplyPart());
        //是否检测微生物
        params.put("${checkGerm}", ExplainConstants.CHECK_GERM);
        params.put("${riskGermEvaluate}", ExplainConstants.RISK_CHECK_GERM_EVALUATE);
        params.put("${germEvaluate}", ExplainConstants.CHECK_GERM_SAFETY_EVALUATE);
        if (!Objects.equals(product.getCheckGerm(), YesOrNoEnum.YES.getCode())) {
            params.put("${checkGerm}", ExplainConstants.NO_CHECK_GERM);
            params.put("${riskGermEvaluate}", ExplainConstants.RISK_NO_CHECK_GERM_EVALUATE);
            params.put("${germEvaluate}", ExplainConstants.NO_CHECK_GERM_SAFETY_EVALUATE);
        }
        params.put("${createTime}", DateUtils.date2Str(product.getCreateTime(), DateUtils.date_sdf_wz.get()));
        params.put("${sysCode}", StringUtils.isEmpty(product.getCompanyCode()) ? product.getSysCode() : product.getCompanyCode() + "-" + product.getSysCode());
        params.put("${usedDsc}", product.getUsedDesc());
        params.put("${ifraClassify}", "/");
        if (StringUtils.isNotEmpty(product.getIfraClassify())) {
            Matcher matcher = Pattern.compile(":").matcher(product.getIfraClassify());
            if (matcher.find()) {
                String ifraClassify = product.getIfraClassify().substring(matcher.start()).replace(":", "");
                params.put("${ifraClassify}", MessageFormat.format(ExplainConstants.CATEGORY_VERSION,
                        ifraClassify,
                        product.getIfraClassify().substring(0, matcher.start())));
            }
        }
        params.put("${dailyDosage}", product.getDailyDosage());
        params.put("${residentDivisor}", product.getResidentDivisor());
        params.put("${exposedReferenceLevel}", product.getExposedReferenceLevel());
        params.put("${safeCaution}", product.getSafeCaution());
        params.put("${useFrequency}", product.getUseFrequency());
        String categoryName = "";
        if (StringUtils.isNotEmpty(product.getCategory()) && product.getCategory().contains(":")) {
            String[] category = product.getCategory().split(":");
            List<CategoryDTO> categoryDTOS = sysBaseApiImpl.queryChildNodesByPCode(category[0]);
            categoryName = categoryDTOS.stream().filter(f -> f.getCode().equalsIgnoreCase(category[1])).map(CategoryDTO::getName).findFirst().orElse("");
        }
        params.put("${category}", categoryName);

        params.put("${department}", "");
        params.put("${appraisedName}", "");
        params.put("${resumeImage}", "");
        params.put("${evaAddress}", "");
        TEvaluator evaluator = evaluatorService.getById(product.getEvaluatorId());
        if (!ObjectUtil.isEmpty(evaluator)) {
            params.put("${department}", evaluator.getDepartment());
            params.put("${appraisedName}", evaluator.getAppraisedName());
            params.put("${resumeImage}", evaluator.getResumeUrl());
            params.put("${evaAddress}", evaluator.getAddress());
        }
        //安评评估结论暴露途径设置
        productExposedWayDeal(params, product, categoryName);

        //所有表格集合
        List<List<String[]>> tableList = Lists.newArrayList();

        //表一、生成原料配方表
        List<TFormula> formulas = formulaService.list(new LambdaQueryWrapper<TFormula>()
                .eq(TFormula::getProductId, product.getId()));
        List<String[]> formalTable = formulas.stream().map(formula ->
                new String[]{String.valueOf(formula.getIdxNum()),
                        formula.getCnName(),
                        formula.getIncicName(),
                        formula.getMajorUsePurpose(),
                        formula.getIecicNum(),
                        formula.getRemark()}
        ).collect(Collectors.toList());
        tableList.add(formalTable);

        //表二、产品实际成分含量表
        List<TSecurityEvaluation> securityEvaluations = securityEvaluationService.list(new LambdaQueryWrapper<TSecurityEvaluation>()
                .eq(TSecurityEvaluation::getProductId, product.getId()));
        securityEvaluations = securityEvaluations.stream()
                .sorted(new Comparator<TSecurityEvaluation>() {
                    @Override
                    public int compare(TSecurityEvaluation o1, TSecurityEvaluation o2) {
                        return new BigDecimal(o2.getContent()).compareTo(new BigDecimal(o1.getContent()));
                    }
                }).collect(Collectors.toList());

        List<String> ingredientNames = securityEvaluations.stream().map(TSecurityEvaluation::getCnName).collect(Collectors.toList());
        params.put("${ingredient}", String.join("、", ingredientNames));
        params.put("${count}", ingredientNames.size());

        List<TSecurityEvaluation> finalSecurityEvaluations = securityEvaluations;
        List<String[]> ingredientTable =
                IntStream.range(0, securityEvaluations.size()).mapToObj(i -> {
                    TSecurityEvaluation securityEvaluation = finalSecurityEvaluations.get(i);
                    TRawmaterial tRawmaterial = rawmaterialService.getOne(new LambdaQueryWrapper<TRawmaterial>()
                            .eq(TRawmaterial::getIecicNum, securityEvaluation.getIecicNum()));
                    return new String[]{String.valueOf(i + 1),
                            securityEvaluation.getCnName(),
                            tRawmaterial.getIncicName(),
                            securityEvaluation.getContent()};
                }).collect(Collectors.toList());
        tableList.add(ingredientTable);

        //表三、配方中各安全成分的评估列表
        List<String[]> accessTable =
                IntStream.range(0, securityEvaluations.size()).mapToObj(i -> {
                    TSecurityEvaluation securityEvaluation = finalSecurityEvaluations.get(i);
                    List<TProductMaterial> productMaterials = getMaterialInfo(securityEvaluation.getId(),
                            IecicLinkedTypeEnum.CIR.getType());
                    String materialInfo = "/";
                    if (!CollectionUtils.isEmpty(productMaterials)) {
                        materialInfo = productMaterials.stream().map(m -> {
                            if (m.getType() == ReferencesEnum.ANNEX.getType()) {
                                return ReferencesEnum.ANNEX.getMsg() + m.getIdxNum();
                            }
                            return String.valueOf(m.getIdxNum());
                        }).collect(Collectors.joining("，"));
                    }
                    return new String[]{String.valueOf(i + 1),
                            securityEvaluation.getCnName(),
                            securityEvaluation.getContent(),
                            securityEvaluation.getTechnicalSpecification(),
                            securityEvaluation.getAuthorityConclusion(),
                            securityEvaluation.getHighestHistoryUsage(),
                            securityEvaluation.getEvaluatedConclusion(),
                            materialInfo};
                }).collect(Collectors.toList());
        tableList.add(accessTable);

        Set<String> riskNameSet = Sets.newHashSet();
        Set<String> harmfulSet = Sets.newHashSet();
        //表四、风险物质表
        List<String[]> riskTable = formulas.stream().map(formula -> {

            String riskMatters = "无";
            String remark = "/";
            List<TFormulaRisk> formulaRisks = formulaRiskService.list(new LambdaQueryWrapper<TFormulaRisk>()
                    .eq(TFormulaRisk::getFormulaId, formula.getId()));
            if (!CollectionUtils.isEmpty(formulaRisks)) {
                List<String> harmful = Arrays.asList(harmfulMatters.split(","));
                //获取风险物质
                formulaRisks.stream().filter(f -> !harmful.contains(f.getRiskName()))
                        .map(TFormulaRisk::getRiskName).collect(Collectors.toCollection(() -> riskNameSet));
                //获取有害物质
                formulaRisks.stream().filter(f -> harmful.contains(f.getRiskName()))
                        .map(TFormulaRisk::getRiskName).collect(Collectors.toCollection(() -> harmfulSet));
                riskMatters = formulaRisks.stream().map(TFormulaRisk::getRiskName).collect(Collectors.joining("、"));

                StringBuilder sb = new StringBuilder();
                sb.append(ExplainConstants.RISK_START_DESC).append(riskMatters).append("\n");
                for (TFormulaRisk formulaRisk : formulaRisks) {
                    if (ObjectUtil.isEmpty(formulaRisk)) {
                        continue;
                    }

                    sb.append(formulaRisk.getRiskName())
                            .append("：")
                            .append("\n");
                    if (StringUtils.isNotEmpty(formulaRisk.getRemark())) {
                        sb.append(formulaRisk.getRemark())
                                .append("\n");
                    }
                    if (StringUtils.isNotEmpty(formulaRisk.getDetailInfo())) {
                        List<String> detailString = JSON.parseObject(formulaRisk.getDetailInfo(), new TypeReference<List<String>>() {
                        });
                        sb.append("详见：");
                        detailString.forEach(msg ->
                                sb.append(msg)
                                        .append("\n")
                        );
                    }
                    List<TProductMaterial> productMaterials = getMaterialInfo(formulaRisk.getId(), IecicLinkedTypeEnum.RISK.getType());
                    if (!CollectionUtils.isEmpty(productMaterials)) {
                        if (StringUtils.isEmpty(formulaRisk.getDetailInfo())) {
                            sb.append("详见：").append("\n");
                        }
                        productMaterials.stream().sorted(Comparator.comparing(TProductMaterial::getIdxNum)).forEach(f ->
                                sb.append(ReferencesEnum.ANNEX.getMsg())
                                        .append(f.getIdxNum())
                                        .append("：")
                                        .append(f.getName())
                                        .append("\n")
                        );
                    }
                    sb.append("\n");
                }
                remark = sb.toString();

            }
            return new String[]{String.valueOf(formula.getIdxNum()),
                    formula.getCnName(),
                    riskMatters,
                    remark};
        }).collect(Collectors.toList());
        tableList.add(riskTable);

        //风险物质总数统计
        params.put("${risk}", String.join("、", riskNameSet));
        params.put("${riskCount}", riskNameSet.size());

        //有害物质过滤
        getHarmfulMattersBuild(params, harmfulSet);

        //查询所有资料
        List<TProductMaterial> productMaterials = productMaterialService.list(new LambdaQueryWrapper<TProductMaterial>()
                .eq(TProductMaterial::getProductId, product.getId())
                .eq(TProductMaterial::getStatus, IsDeleteEnum.NORMAL.getCode())
                .or().isNull(TProductMaterial::getProductId));
        if (!CollectionUtils.isEmpty(productMaterials)) {

            int maxNum = productMaterials.stream()
                    .filter(f -> f.getType() == ReferencesEnum.DOC.getType())
                    .mapToInt(TProductMaterial::getIdxNum).max().orElse(0);

            //获取文献列表
            List<Object> docList = productMaterials.stream()
                    .filter(f -> f.getType() == ReferencesEnum.DOC.getType())
                    .peek(m -> {
                        if (m.getProductId() == null) {
                            m.setIdxNum(maxNum + 1);
                        }
                    }).sorted(Comparator.comparing(TProductMaterial::getIdxNum).reversed())
                    .map(sb -> new StringBuilder().append(sb.getIdxNum())
                            .append(".")
                            .append(sb.getName())).collect(Collectors.toList());
            params.put("${docList}", CollectionUtils.isEmpty(docList) ? "" : docList);

            //获取附录列表
            List<Object> annexList = productMaterials.stream()
                    .filter(f -> f.getType() == ReferencesEnum.ANNEX.getType())
                    .sorted(Comparator.comparing(TProductMaterial::getIdxNum).reversed())
                    .map(m -> new StringBuilder().append(ReferencesEnum.ANNEX.getMsg())
                            .append(m.getIdxNum())
                            .append(m.getName())).collect(Collectors.toList());
            params.put("${annexList}", CollectionUtils.isEmpty(annexList) ? "" : annexList);
        }
        try {
            String fileName = String.format("%s.docx", product.getSysCode());
            String path = uploadPath + "/" + fileName;
            return XwpfTUtil.process(path, params, new int[]{2, 3, 4, 5}, tableList);
        } catch (Exception e) {
            log.error("报告生成异常:{}", e.getMessage());
            return null;
        }
    }

    /**
     * 查询关联文献和附录
     *
     * @param linkedId
     * @param linkedType
     * @return
     */
    private List<TProductMaterial> getMaterialInfo(Long linkedId, Integer linkedType) {
        Map<String, Object> map = Maps.newHashMap();
        map.put("linkedId", linkedId);
        map.put("linkedType", linkedType);
        return productMaterialService.getProductMaterialList(map);
    }

    /**
     * 暴露途径参数设置
     *
     * @param params
     * @param product
     * @param categoryName
     */
    private void productExposedWayDeal(Map<String, Object> params, TProduct product, String categoryName) {
        if (StringUtils.isEmpty(product.getExposedWay())) {
            params.put("${exposedWay}", "");
            return;
        }
        List<String> types = Arrays.asList(product.getExposedWay().split(","));
        types = types.stream().filter(f -> Integer.valueOf(f) != ExposedWayEnum.PERTACUNEOUS.getWayType()).collect(Collectors.toList());
        String exposedWayMsg;
        if (CollectionUtils.isEmpty(types)) {
            String desc = String.format("可%s使用。" + ExplainConstants.EXPOSED_WAY_PERTACUNEOUS, product.getUsedDesc());
            exposedWayMsg = MessageFormat.format(ExplainConstants.EXPOSED_WAY_COMMON,
                    categoryName,
                    ProductTypeEnum.getProductTypeEnumByType(product.getProductType()).getMsg(),
                    product.getApplyPart(),
                    desc);
            params.put("${exposedWay}", exposedWayMsg);
            return;
        }
        if (types.size() > 1) {
            params.put("${exposedWay}", "");
            return;
        }
        String type = types.get(0);
        if (Integer.valueOf(type) == ExposedWayEnum.INHALATION.getWayType()) {
            exposedWayMsg = MessageFormat.format(ExplainConstants.EXPOSED_WAY_COMMON,
                    categoryName,
                    ProductTypeEnum.getProductTypeEnumByType(product.getProductType()).getMsg(),
                    product.getApplyPart(),
                    ExplainConstants.EXPOSED_WAY_PERTACUNEOUS_INHALATION);
        } else {
            exposedWayMsg = MessageFormat.format(ExplainConstants.EXPOSED_WAY_COMMON,
                    categoryName,
                    ProductTypeEnum.getProductTypeEnumByType(product.getProductType()).getMsg(),
                    product.getApplyPart(),
                    ExplainConstants.EXPOSED_WAY_PERTACUNEOUS_PERORAL);
        }
        params.put("${exposedWay}", exposedWayMsg);
    }

    /**
     * 有害物质过滤
     *
     * @param params
     * @param formulaRisks
     */
    private void getHarmfulMattersBuild(Map<String, Object> params, Set<String> formulaRisks) {
        if (CollectionUtils.isEmpty(formulaRisks)) {
            params.put("${harmfulMatter}", "铅、汞、砷、镉");
            return;
        }
        params.put("${harmfulMatter}", "铅、汞、砷、镉" + "、" + String.join("、", formulaRisks));
    }

}
